package com.capsulode.excel.sheetreader;

import com.capsulode.excel.annotation.Header;
import com.capsulode.excel.annotation.RowNum;
import com.capsulode.excel.conversion.ConverterManager;
import com.capsulode.excel.conversion.Reader;
import com.capsulode.excel.conversion.Validator;
import com.capsulode.excel.exception.RowWrappingFailedException;
import com.capsulode.excel.exception.UnsupportedConversionException;
import com.capsulode.excel.exception.ValidationFailedException;
import com.capsulode.excel.header.AnnotatedHeader;
import org.apache.commons.beanutils.BeanUtils;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Set;

public class AnnotatedSheetReader<R> extends AbstractSheetReader<R> {
    private static javax.validation.Validator jsrValidator = Validation.buildDefaultValidatorFactory().getValidator();

    private final RowConsumer<R> consumer;

    public AnnotatedSheetReader(Class<R> type, RowConsumer<R> consumer) {
        if (type == null) {
            throw new IllegalArgumentException("Excel row data wrapper bean type not specified.");
        }
        if (consumer == null) {
            consumer = (rowNum) -> {
                try {
                    R rowObj = type.newInstance();
                    reflectRowNum(type, rowObj, rowNum);
                    return rowObj;
                } catch (ReflectiveOperationException ex) {
                    throw new RowWrappingFailedException(ex);
                }
            };
        }
        this.consumer = consumer;

        for (Field field : type.getDeclaredFields()) {
            Header headerDefine = field.getAnnotation(Header.class);
            if (headerDefine != null) {
                String headerName = headerDefine.value();
                Class headerType = field.getType();
                String fieldName = field.getName();

                Reader reader = null;
                try {
                    reader = ConverterManager.newConverter(headerType, headerDefine);
                } catch (UnsupportedConversionException e) {
                    throw new IllegalArgumentException("不能识别的字段类型：" + headerType, e);
                }
                Validator validator = value -> {
                    Set<ConstraintViolation<R>> constraintViolations = jsrValidator.validateValue(type, fieldName, value);
                    if (!constraintViolations.isEmpty()) {
                        throw new ValidationFailedException(constraintViolations.iterator().next().getMessage());
                    }
                };
                header(new AnnotatedHeader<R, Object>(headerName, fieldName, headerType, reader, validator,headerDefine.columnIndex()));
            }
        }
    }

    private void reflectRowNum(Class<R> type, R rowObj, int rowNum) throws InvocationTargetException, IllegalAccessException {
        for (Field f : type.getDeclaredFields()) {
            if (f.getAnnotation(RowNum.class) != null) {
                BeanUtils.copyProperty(rowObj, f.getName(), rowNum);
            }
        }
    }

    @Override
    protected R newRow(int rowNum) throws RowWrappingFailedException {
        return consumer.newRow(rowNum);
    }
}
